# -*- coding: utf-8 -*-
"""
状态管理模块
"""

_state_listeners = []# 小黑板：想听状态变化，就把自己的函数挂上来

import json
import os
import time
import threading
import datetime
import csv
import glob
import shutil
import tempfile
from pathlib import Path

import psutil
import ctypes
from ctypes import wintypes
from utils.common_tools import get_idle_seconds
from utils.logger import log
from utils.input_dialogs import input_box, combo_input_box,multi_input_box
from utils.constants import (
    CSV_ROW_TEMPLATE, EXIT_PWD, STATE_MAP, STATE_COLOR, WORK_STATES,
    ROLE_MAP, GRACE_SECS, MUTEX_NAME,CSV_HEADER)
from services.file_operations import update_last_row_end_time
from core.signal_hub import add_state_listener

# 全局状态变量
_t0 = None
_phase = 0
_active_start_time = None  # 活动开始时间
_total_active_time = 0     # 累计活动时间
state = None
start = None
task_id_buffer = ''
remark_buffer = ''
switch_type_buffer = '手动'
_last_work = 'DataWork'
_work_start_t = None
_work_accum_s = 0
_last_notify_time = 0
_state_task_id = {}
_data_dir = None
_USER_ID = None 
_log_dir = None
_GUARD_EXIT = threading.Event()
_current_task_key = None
_TASK_IDENTIFIER_FILE = None

# 空闲检测相关
_t0 = None
_phase = 0
grace_left = 0

# 线程控制
_input_active = threading.Event()

def add_state_listener(func): #钩子函数
    """别的模块想刷新颜色，就调用这个登记"""
    _state_listeners.append(func)

# 在 state_manager.py 中找到 get_task_key 函数，修改它：

def get_task_key(project_name, work_type, task_id, workload2=''):
    """
    生成任务唯一标识
    规则：项目名称、作业类型、任务ID 三个字段必须至少填写两个
    空值的字段不参与比较
    """
    # 创建一个列表来存储填写了的字段
    filled_fields = []
    
    # 检查项目名称是否填写（去空格后不为空）
    if project_name and project_name.strip():
        filled_fields.append(f"项目:{project_name.strip()}")
    
    # 检查作业类型是否填写
    if work_type and work_type.strip():
        filled_fields.append(f"类型:{work_type.strip()}")
    
    # 检查任务ID是否填写
    if task_id and task_id.strip():
        filled_fields.append(f"ID:{task_id.strip()}")
    
    # ★★★ 新增：检查工作量2是否填写
    if workload2 and workload2.strip():
        filled_fields.append(f"框量:{workload2.strip()}")
    
    # 检查是否至少填写了两个字段（不包括工作量2）
    required_fields = [f for f in filled_fields if not f.startswith('框量:')]
    if len(required_fields) < 2:
        # 如果必填字段少于2个，返回None表示不符合要求
        return None
    
    # 对字段进行排序（确保顺序不影响比较），然后用"|"连接
    return "|".join(sorted(filled_fields))

# 原有的全局变量保持不变
_state_listeners = []  # 小黑板：想听状态变化，就把自己的函数挂上来

def init_state_manager(data_dir, user_id): #初始化状态管理器
    """ 初始化全部全局状态变量"""

    global _DATA_DIR, _USER_ID, _log_dir, _state_task_id, _input_active
    global task_id_buffer, remark_buffer, switch_type_buffer, _last_work
    global _work_start_t, _work_accum_s, _last_notify_time, _t0, _phase, grace_left
    global _current_task_key, _TASK_IDENTIFIER_FILE  # 新增
    
    _DATA_DIR = data_dir
    _USER_ID  = user_id
    _log_dir  = os.path.join(data_dir, 'logs')
    _state_task_id = {st: '' for st in WORK_STATES}
    
    # 新增：任务标识存储文件路径
    _TASK_IDENTIFIER_FILE = Path(data_dir) / "task_identifier.json"
    
    # 初始化变量
    state = None
    start = None
    task_id_buffer = ''
    remark_buffer = ''
    switch_type_buffer = '手动'
    _last_work = 'DataWork'
    _work_start_t = None
    _work_accum_s = 0
    _last_notify_time = 0
    _t0 = None
    _phase = 0
    grace_left = 0
    _current_task_key = None
    
    # 新增：加载之前保存的任务标识
    _load_task_identifier()
    
    _input_active = threading.Event()
    _input_active.set()
    from services.file_operations import register_close_segment_callback
    register_close_segment_callback(_close_previous_segment)

    # 创建目录
    os.makedirs(_log_dir, exist_ok=True)

def _load_task_identifier():
    """加载之前保存的任务标识"""
    global _current_task_key, task_id_buffer, _state_task_id
    
    if _TASK_IDENTIFIER_FILE and _TASK_IDENTIFIER_FILE.exists():
        try:
            with open(_TASK_IDENTIFIER_FILE, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            _current_task_key = data.get('current_task_key')
            task_id_buffer = data.get('task_id_buffer', '')
            
            # 恢复各状态的任务ID
            state_task_data = data.get('state_task_id', {})
            for state_key, task_id in state_task_data.items():
                if state_key in _state_task_id:
                    _state_task_id[state_key] = task_id
            
        except Exception as e:
            log(f'[ERR] 加载任务标识失败: {e}')
            _current_task_key = None

def _save_task_identifier():
    """保存当前任务标识到文件"""
    if not _TASK_IDENTIFIER_FILE:
        return
    
    try:
        data = {
            'current_task_key': _current_task_key,
            'task_id_buffer': task_id_buffer,
            'state_task_id': _state_task_id,
            'last_update': datetime.datetime.now().isoformat(),
            'user_id': _USER_ID
        }
        
        with open(_TASK_IDENTIFIER_FILE, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        log(f'[TASK] 已保存任务标识: {_current_task_key}')
        
    except Exception as e:
        log(f'[ERR] 保存任务标识失败: {e}')

def set_state(st, manual=True, auto_task_id=''):
    """状态切换唯一入口：写 CSV、刷新托盘/悬窗"""
    import traceback
    global state, start, task_id_buffer, remark_buffer, switch_type_buffer
    global _last_work, _work_start_t, _work_accum_s, _state_task_id, grace_left
    global _current_task_key

    now   = datetime.datetime.now()
    same_state_switch = (st == state)
    switch_type_buffer = '手动' if manual else '自动'

    # 默认值
    new_task_id  = ''
    project_name = ''
    work_type    = ''
    workload1    = ''
    workload2    = ''  # ★★★ 新增：工作量2
    new_remark   = ''

    if st in WORK_STATES:
        _last_work = st
        if manual:
            # ★★★ 新增：如果有当前任务标识，自动解析并填充默认值（包括工作量2）
            default_values = _get_default_values_from_task_key()
            
            default_work_type = default_values.get('work_type', '')
            default_project = default_values.get('project_name', '')
            default_task_id = default_values.get('task_id', '')
            default_workload2 = default_values.get('workload2', '')  # ★★★ 新增：工作量2
            
            # ① 先选作业类型（下拉）
            work_type = combo_input_box('作业类型', '请选择本次作业类型：',
                                        ['数据更新', '初审', '复审'],
                                        default_value=default_work_type)
            if work_type is None:
                log('[INFO] 用户取消选择作业类型，状态切换中止')
                return

            # ② 再问剩下 5 项（带默认值）
            prompts = ['项目名称', '任务ID', '工作量1', '初始框量', '备注']  # ★★★ 修改：改为5个提示
            default_prompts = [default_project, default_task_id, '', default_workload2, '']  # ★★★ 修改：传入工作量2默认值
            
            values = multi_input_box(f'输入{STATE_MAP.get(st, st)}信息', prompts, default_prompts)
            if values is None:
                log('[INFO] 用户取消输入，状态切换已中止')
                return

            # ③ 拆包 + 简单校验
            project_name, task_id, workload1, workload2, new_remark = values  # ★★★ 修改：拆包5个值
            
            # ★★★ 修改：如果用户没有填写，使用默认值
            if not project_name.strip() and default_project:
                project_name = default_project
                log(f'[TASK] 使用默认项目名称: {project_name}')
            
            if not task_id.strip() and default_task_id:
                task_id = default_task_id
                log(f'[TASK] 使用默认任务ID: {task_id}')
            
            if not work_type and default_work_type:
                work_type = default_work_type
                log(f'[TASK] 使用默认作业类型: {work_type}')
            
            if not workload2.strip() and default_workload2:
                workload2 = default_workload2
                log(f'[TASK] 使用默认框量: {workload2}')

            # ★★★ 新增：验证任务ID格式（如果填写了的话）
            if task_id.strip() and not task_id.strip().isdigit():
                log('[INFO] 任务ID必须为数字，状态切换已中止')
                # 给用户提示
                from PyQt6.QtWidgets import QMessageBox
                QMessageBox.warning(None, "输入错误", "任务ID必须为数字！\n请重新选择状态。")
                return
            
            # ★★★ 修改：验证必填字段数量（现在包括工作量2）
            # ★★★ 修改这里：生成任务标识时传入 workload2
            task_key = get_task_key(project_name, work_type, task_id, workload2)  # ★★★ 修改：传入workload2
            if task_key is None:
                log('[INFO] 必填字段不足两个，状态切换已中止')
                # 给用户提示
                from PyQt6.QtWidgets import QMessageBox
                QMessageBox.warning(None, "输入不完整", 
                                  "请至少填写以下三项中的两项：\n"
                                  "• 项目名称\n" 
                                  "• 作业类型\n"
                                  "• 任务ID\n\n"
                                  "状态切换已取消，当前计时继续。")
                return

            # ④ 真正赋值
            project_name = project_name.strip()
            new_task_id  = task_id.strip()
            workload1    = workload1.strip()
            workload2    = workload2.strip()  # ★★★ 新增：工作量2
            new_remark   = new_remark.strip()

        else:  # 自动切换
            new_task_id = auto_task_id or _state_task_id.get(st, '')
            
            # ★★★ 修复：自动切换时从任务标识解析项目名称和作业类型
            if _current_task_key:
                defaults = _get_default_values_from_task_key()
                project_name = defaults.get('project_name', '')
                work_type = defaults.get('work_type', '')
                workload2 = defaults.get('workload2', '')  # ★★★ 新增：工作量2
                log(f'[AUTO] 从任务标识解析: 项目|{project_name}, 类型|{work_type}, ID|{new_task_id}, 框量|{workload2}')
            else:
                log(f'任务标识解析失败')

    elif manual and st not in WORK_STATES and st != 'Break':
        prompt_map = {
            'Meeting':  '请输入本次[开会]主题/议题:',
            'Training': '请输入本次[培训]主题/议题:',
            None:       '请输入本次[协助管理]备注:'
        }
        prompt = prompt_map.get(st, f'请输入本次[{STATE_MAP.get(st, st)}]备注:')
        remark_input = input_box('状态备注', prompt)
        new_remark = remark_input.strip() if remark_input else ''

    # ★★★ 修改：计算当前任务唯一标识（包含工作量2）
    if st in WORK_STATES:
        if manual:
            new_task_key = get_task_key(project_name, work_type, new_task_id, workload2)  # ★★★ 修改：传入workload2
        else:
            new_task_key = get_task_key(project_name, work_type, new_task_id, workload2)  # ★★★ 修改：传入workload2
            if new_task_key is None and _current_task_key:
                # 自动切换时优先使用当前任务标识
                new_task_key = _current_task_key
                log(f'[AUTO] 自动切换使用当前任务标识: {new_task_key}')
    else:
        new_task_key = None
    
    # ★★★ 修改：检查任务标识是否变化
    task_key_changed = (new_task_key != _current_task_key)
    
    # 如果任务标识发生变化，强制结束当前段并开始新段
    if task_key_changed and start and st in WORK_STATES:
        log(f'[TASK] 任务标识变化: {_current_task_key} -> {new_task_key}，结束当前段')
        _close_previous_segment(now)
        _work_start_t, _work_accum_s = None, 0

    # 更新当前任务标识
    if st in WORK_STATES and new_task_key is not None:
        _current_task_key = new_task_key
        log(f'[TASK] 更新当前任务标识: {_current_task_key}')
        _save_task_identifier()
    else:
        log(f'[TASK] last任务标识: {_current_task_key}')

    # —— 修改原有逻辑，考虑任务标识变化 ——
    # 任务变化的条件：任务ID变化 或 任务标识变化
    task_changed = (new_task_id != task_id_buffer) or task_key_changed

    # ★★★ 修改：只有状态相同且任务标识未变化时才继续当前段
    if same_state_switch and start and st in WORK_STATES and not task_key_changed:
        # 状态相同、是作业状态、且任务标识未变化：继续当前计时段
        log(f'[TASK] 相同任务标识，继续计时: {_current_task_key}')
        if task_changed:
            # 理论上这里不会执行，因为task_key_changed为False时task_changed也应该为False
            _close_previous_segment(now)
            _start_new_segment(st, now, new_task_id, project_name, work_type, workload1, workload2, new_remark)  # ★★★ 修改：传入workload2
            task_id_buffer = new_task_id
            if st in WORK_STATES:
                _state_task_id[st] = new_task_id
        else:
            # 完全相同的任务：重置工作计时器，继续计时
            _work_start_t, _work_accum_s = now, 0
        state, start = st, now
        _work_start_t, _work_accum_s = now, 0
        return

    # 其他情况：结束当前段并开始新段
    if start:
        _close_previous_segment(now)

    # 更新任务ID缓冲区
    task_id_buffer = new_task_id
    if st in WORK_STATES:
        _state_task_id[st] = new_task_id

    # 开始新的时间段
    _start_new_segment(st, now, new_task_id, project_name, work_type, workload1, workload2, new_remark)  # ★★★ 修改：传入workload2
    state, start = st, now
    _work_start_t, _work_accum_s = now, 0

    def _refresh_all():
        # 通过信号中心通知所有监听器
        from core.signal_hub import fire_state_changed
        fire_state_changed(state)
        
        # 同时直接调用现有的监听器（兼容旧代码）
        for func in _state_listeners:
            try:
                func(state)
            except Exception as e:
                log(f'[ERR] 状态监听器执行失败: {e}')
    
    _refresh_all()

    if manual:
        grace_left = 60

# 在 state_manager.py 中找到 _start_new_segment 函数，修改它：

def _start_new_segment(st, start_time, task_id, project_name='', work_type='', workload1='', workload2='', remark=''):
    """开始新的时间段，写入CSV记录"""
    global _USER_ID, _log_dir, switch_type_buffer, _current_task_key
    date = start_time.strftime('%Y-%m-%d')
    fname = os.path.join(_log_dir, f'{date}_{_USER_ID}.csv')
    
    # 必须保留这两行！
    role = ROLE_MAP.get(_USER_ID[:2], '管理员')
    work_flag = '作业' if st in WORK_STATES else '非作业'
    
    # 修改这里：作业状态统一记录为"作业"
    if st in WORK_STATES:
        display_state = '作业'  # 所有作业状态统一显示为"作业"
    else:
        display_state = st  # 按钮值

    # 准备数据用于调试日志
    row = CSV_ROW_TEMPLATE.copy()
    row[0] = date  # 日期
    row[1] = _USER_ID  # 工号
    row[2] = role  # 角色
    row[3] = work_flag  # 作业/非作业
    row[4] = display_state  # 状态 - 使用修改后的显示状态
    row[5] = start_time.strftime('%H:%M:%S')  # 开始时间
    row[6] = ''  # 结束时间空
    row[7] = '0'  # 时长0
    row[8] = switch_type_buffer  # 主被动切换
    row[9] = project_name  # 项目名称
    row[10] = work_type  # 作业类型
    row[11] = task_id  # 任务ID
    row[12] = workload1  # 工作量1
    row[13] = workload2  # ★★★ 修改：工作量2
    # 工作量3(14)留空（会自动计算）
    row[15] = remark  # 备注
    
    # 只调用一次 flush_one_record 来写入文件
    from services.file_operations import flush_one_record
    flush_one_record(
        fname, _USER_ID, st, start_time, None, 0, date,
        task_id, remark, switch_type_buffer,
        project_name, work_type, workload1, workload2  # ★★★ 修改：传入workload2
    )
    
    log(f'[STATE] 开始新时间段: {display_state}')

    from core.signal_hub import fire_state_changed
    fire_state_changed(st)

def start_background_threads(): 
    """启 5 条后台线程（刷新、空闲、日清、30 min 落盘、通知）"""
    threads = [
        threading.Thread(target=_refresh_loop, daemon=True, name="RefreshLoop"),
        threading.Thread(target=_idle_monitor, daemon=True, name="IdleMonitor"),
        threading.Thread(target=_daily_clean, daemon=True, name="DailyClean"),
        threading.Thread(target=_half_hour_flush, daemon=True, name="HalfHourFlush"),  
        threading.Thread(target=_notification_monitor, daemon=True, name="NotificationMonitor"),
    ]
    
    for t in threads:
        t.start()
        
    # ★★★ 确保自动保存系统启动
    _setup_auto_save_system()

def _idle_monitor():
    """空闲监控 - 永不退出版"""
    try:
        _real_idle_monitor()
    except Exception as e:
        log(f'[THREAD-CRASH] _idle_monitor: {e}')

def _real_idle_monitor():
    """真正的空闲逻辑 - 修复自动恢复问题"""
    global state, _t0, _phase, grace_left, _last_work, _state_task_id, _input_active
    global _active_start_time, _total_active_time, _current_task_key, _auto_switch_task_info
    
    _last_log_time = 0
    _last_activity_log = 0
    loop_count = 0
    
    while True:
        time.sleep(1)
        loop_count += 1
        
        # 调试日志
        if loop_count % 60 == 0:
            idle = get_idle_seconds()
        
        # 重置条件
        if state is None:
            _t0, _phase = None, 0
            _active_start_time, _total_active_time = None, 0
            continue
            
        if state in WORK_STATES:
            # 工作状态下的空闲检测
            if idle >= 120:
                log('[AUTO] 2分钟无操作，自动切换到休息')
                # 保存当前任务信息
                _auto_switch_task_info = {
                    'project_name': '',
                    'work_type': '', 
                    'task_id': task_id_buffer,
                    'last_work_state': state,
                    'last_task_key': _current_task_key,
                    'switch_time': datetime.datetime.now()
                }
                if _current_task_key:
                    defaults = _get_default_values_from_task_key()
                    _auto_switch_task_info['project_name'] = defaults.get('project_name', '')
                    _auto_switch_task_info['work_type'] = defaults.get('work_type', '')
                    
                log(f'[AUTO] 保存任务信息: {_auto_switch_task_info}')
                set_state('Break', manual=False)
            _t0, _phase = None, 0
            _active_start_time, _total_active_time = None, 0
            continue
            
        if grace_left > 0:
            grace_left -= 1
            _t0, _phase = None, 0
            _active_start_time, _total_active_time = None, 0
            continue
            
        if not _input_active.is_set():
            continue
            
        idle = get_idle_seconds()  # 每次循环都获取空闲时间
            
        # 阶段0：检测到活动开始
        if _phase == 0 and idle < 5:
            log(f'[AUTO] 检测到活动，开始累计活动计时')
            _t0 = time.time()
            _active_start_time = time.time()
            _total_active_time = 0
            _phase = 1
            _last_activity_log = time.time()
            continue
            
        # 阶段1：累计活动时间
        if _phase == 1:
            current_time = time.time()
            total_elapsed = current_time - _t0
            
            # 更新活动时间
            if idle < 5:
                _total_active_time = current_time - _active_start_time
                
            # 定期日志
            if current_time - _last_activity_log >= 20:  # 改为20秒记录一次
                log(f'[AUTO] 累计活动{int(_total_active_time)}/300秒')
                _last_activity_log = current_time

        if _total_active_time >= 60:
            log(f'[AUTO] ✅ 满足条件！累计活动{_total_active_time:.1f}秒 ≥ 60秒')
            if _last_work and _last_work in {'DataWork', 'Rework'}:   # 只认这两个作业状态
                # 优先恢复到上一次作业状态
                if (_auto_switch_task_info and
                        _auto_switch_task_info.get('last_work_state') in {'DataWork', 'Rework'}):
                    previous_task_id = _auto_switch_task_info.get('task_id', '')
                    project_name = _auto_switch_task_info.get('project_name', '')
                    work_type = _auto_switch_task_info.get('work_type', '')
                    set_state(_auto_switch_task_info['last_work_state'], manual=False,
                            auto_task_id=previous_task_id)
                else:
                    # 没有有效任务信息则默认回到 DataWork
                    set_state('DataWork', manual=False)
                # 重置计数器
                _t0, _phase = None, 0
                _active_start_time, _total_active_time = None, 0
                
            elif total_elapsed >= 300:  # 5分钟超时
                log(f'[AUTO] ❌ 超时重置：5分钟内未累计满1分钟活动')
                _t0, _phase = None, 0
                _active_start_time, _total_active_time = None, 0

def _show_auto_recovery_dialog(last_work_state):
    """自动恢复时弹窗让用户填写任务信息"""
    
    log(f'[AUTO] 显示自动恢复弹窗，状态: {last_work_state}')
    log(f'[AUTO-DEBUG] 当前任务信息: {_auto_switch_task_info}')

    # 确保在主线程中执行
    from PyQt6.QtCore import QTimer
    QTimer.singleShot(0, lambda: _real_show_auto_recovery_dialog(last_work_state))
    
def _real_show_auto_recovery_dialog(last_work_state):
    """实际的自动恢复对话框"""
    try:
        # 使用和手动切换相同的输入流程
        work_type = combo_input_box('作业类型', '检测到活动，请选择作业类型：',
                                    ['数据更新', '初审', '复审'])
        if work_type is None:
            log('[AUTO] 用户取消自动恢复')
            return

        # 询问其他信息（不传默认值，因为这是首次填写）
        prompts = ['项目名称', '任务ID', '工作量1', '备注']
        values = multi_input_box(f'输入{STATE_MAP.get(last_work_state, last_work_state)}信息', prompts)
        if values is None:
            log('[AUTO] 用户取消自动恢复输入')
            return

        project_name, task_id, workload1, remark = values
        
        # 验证任务ID格式（如果填写了的话）
        if task_id.strip() and not task_id.strip().isdigit():
            log('[AUTO] 任务ID必须为数字，自动恢复中止')
            from PyQt6.QtWidgets import QMessageBox
            QMessageBox.warning(None, "输入错误", "任务ID必须为数字！\n自动恢复已取消。")
            return
        
        # 验证必填字段数量
        task_key = get_task_key(project_name, work_type, task_id)
        if task_key is None:
            log('[AUTO] 必填字段不足两个，自动恢复中止')
            from PyQt6.QtWidgets import QMessageBox
            QMessageBox.warning(None, "输入不完整", 
                              "请至少填写以下三项中的两项：\n"
                              "• 项目名称\n" 
                              "• 作业类型\n"
                              "• 任务ID\n\n"
                              "自动恢复已取消。")
            return

        # 赋值
        project_name = project_name.strip()
        task_id = task_id.strip()
        workload1 = workload1.strip()
        remark = remark.strip()

        # ★★★ 修复：直接写入CSV文件，确保所有信息都被记录
        now = datetime.datetime.now()
        date = now.strftime('%Y-%m-%d')
        fname = os.path.join(_log_dir, f'{date}_{_USER_ID}.csv')
        
        log(f'[AUTO] 准备写入记录 - 项目: {project_name}, 类型: {work_type}, 任务ID: {task_id}')
        
        # 直接调用文件操作函数写入完整信息
        from services.file_operations import flush_one_record
        flush_one_record(
            fname, _USER_ID, last_work_state, now, None, 0, date,
            task_id, remark, '自动',  # 注意：自动切换
            project_name, work_type, workload1
        )
        
        # ★★★ 更新全局状态变量
        global state, start, _current_task_key, _auto_switch_task_info, task_id_buffer
        state = last_work_state
        start = now
        task_id_buffer = task_id
        
        # 更新任务标识
        _current_task_key = get_task_key(project_name, work_type, task_id)
        
        # 更新自动切换任务信息
        _auto_switch_task_info = {
            'project_name': project_name,
            'work_type': work_type, 
            'task_id': task_id,
            'last_work_state': last_work_state,
            'last_task_key': _current_task_key,
            'switch_time': datetime.datetime.now()
        }
        
        # ★★★ 保存任务标识到文件
        _save_task_identifier()
        
        # ★★★ 触发颜色更新
        from core.signal_hub import fire_state_changed
        fire_state_changed(state)
        
        log(f'[AUTO] 自动恢复完成 - 状态: {state}, 任务标识: {_current_task_key}')
        log(f'[AUTO] 已记录完整信息 - 项目: {project_name}, 类型: {work_type}, 任务ID: {task_id}')
        
    except Exception as e:
        log(f'[AUTO-ERROR] 自动恢复对话框异常: {e}')
        import traceback
        traceback.print_exc()

def log_end(): #补填 CSV 行尾时间戳
    """结束当前记录"""
    global start, state
    
    try:
        if not start:
            log('[INFO] 无活动计时，无需保存')
            return
        
        end = datetime.datetime.now()
        secs = int((end - start).total_seconds())
        date = start.strftime('%Y-%m-%d')

        # 先补填上一行的结束时间
        _close_previous_segment(end)
        
        log(f'[SAVE] 已保存记录: {STATE_MAP.get(state, state)} 时长: {secs}秒')
        
    except Exception as e:
        log(f'[ERR] log_end: {e}')
    finally:
        start = None

def _close_previous_segment(end_time): #import用函数填写结束时间
    if not start:
        return
    fname = os.path.join(_log_dir, f'{start.strftime("%Y-%m-%d")}_{_USER_ID}.csv')
    if os.path.exists(fname):
        update_last_row_end_time(fname, end_time)

def _update_last_record_end_time(end_time):
    if not start:
        log('[AUTO-SAVE] 无开始时间，跳过更新')
        return
        
    fname = os.path.join(_log_dir, f'{start.strftime("%Y-%m-%d")}_{_USER_ID}.csv')
    if os.path.exists(fname):
        update_last_row_end_time(fname, end_time)
    else:
        log('[AUTO-SAVE] CSV文件不存在，跳过更新')

def _refresh_loop():
    """刷新循环 - 永不退出"""
    try:
        while True:
            time.sleep(1)
    except Exception as e:
        log(f'[THREAD-CRASH] _refresh_loop: {e}')

def set_initial_state(user_id: str):
    """设置初始状态 - 修复自动切换信息初始化"""
    global _USER_ID, _last_work, _auto_switch_task_info
    
    _USER_ID = user_id
    _last_work = 'DataWork'
    
    # ★★★ 确保自动切换信息正确初始化
    _auto_switch_task_info = {
        'last_work_state': _last_work,
        'last_task_key': None,
        'task_id': '',
        'switch_time': datetime.datetime.now()
    }
    
    # 设置初始状态为休息
    set_state('Break', manual=False)

def _daily_clean():
    """每日清理 - 永不退出"""
    try:
        while True:
            now = datetime.datetime.now()
            tomorrow = (now + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=5)
            time.sleep((tomorrow - now).total_seconds())
            log_end()
    except Exception as e:
        log(f'[THREAD-CRASH] _daily_clean: {e}')

def _half_hour_flush():
    """30 分钟落盘 - 永不退出"""
    global state, _work_start_t, task_id_buffer, _work_accum_s
    try:
        while True:
            time.sleep(3600)
            if not state or _work_start_t is None:
                continue
            now = datetime.datetime.now()
            _close_previous_segment(now)
            _start_new_segment(state, now, task_id_buffer)
            _work_accum_s = 0
            _work_start_t = now
            log(f"[60min_FLUSH] 已落盘并继续计时：{state}")
    except Exception as e:
        log(f'[THREAD-CRASH] _half_hour_flush: {e}')

def _notification_monitor():
    """通知监控 - 永不退出"""
    try:
        while True:
            time.sleep(60)
            _check_work_duration_notification()
    except Exception as e:
        log(f'[THREAD-CRASH] _notification_monitor: {e}')

def _check_work_duration_notification():
    """检查工作持续时间通知"""
    global state, _work_start_t, _work_accum_s, _last_notify_time
    
    if not state or state not in WORK_STATES or _work_start_t is None:
        return

    continuous_sec = _work_accum_s + (datetime.datetime.now() - _work_start_t).total_seconds()
    continuous_h = continuous_sec / 3600

    # 10 分钟冷却
    if time.time() - _last_notify_time < 600:
        return

    if continuous_h >= 5 and _last_notify_time < 4*3600:
        _show_work_reminder("", "长时间工作提醒")
        _last_notify_time = time.time()
    elif continuous_h >= 3 and _last_notify_time < 2*3600:
        _show_work_reminder("", "适度休息提醒")
        _last_notify_time = time.time()
    elif continuous_h >= 1 and _last_notify_time == 0:
        _show_work_reminder("", "温馨提醒")
        _last_notify_time = time.time()

def _show_work_reminder(message, title):
    """显示工作提醒"""
    emoji_messages = {
        "长时间工作提醒": "💖 您已经连续工作5小时了！\n请停下来，活动一下身体吧～\n喝杯水，看看远方，让眼睛休息一下",
        "适度休息提醒": "🌸 您已经连续工作3小时了！\n休息5分钟吧，起来走动一下～\n活动身体对健康很重要哦", 
        "温馨提醒": "😊 您已经连续工作1小时了！\n注意休息一下哦～\n眨眨眼，放松一下肩膀"
    }
    
    friendly_message = emoji_messages.get(title, f"💕 {message}")
    _show_bubble_notification(friendly_message, title)

def _show_bubble_notification(message, title="温馨提示"):
    """显示气泡通知"""
    log(f'[BUBBLE] {title}: {message}')

def _setup_auto_save_system():
    """设置自动保存系统"""
    threading.Thread(target=_frequent_end_time_update, daemon=True, name="AutoSave").start()
    threading.Thread(target=_hourly_checkpoint, daemon=True, name="HourlyCheckpoint").start()
    log('[AUTO-SAVE] 自动保存系统已启动：30秒更新结束时间 + 1小时检查点')

def _frequent_end_time_update(): #高频结束更新时间
    global start, state
    
    
    update_count = 0  # 新增：更新计数器
    
    while not _GUARD_EXIT.wait(30):
        try:
            update_count += 1
            if start and state is not None:
                now = datetime.datetime.now()
                log(f'[AUTO-SAVE] 更新结束时间: {now.strftime("%H:%M:%S")}, 状态: {state}')
                _update_last_record_end_time(now)
            else:
                log(f'[AUTO-SAVE] 第{update_count}次检查: 无活动计时 (start={start}, state={state})')
        except Exception as e:
            log(f'[ERR] 高频更新失败: {e}')

def _hourly_checkpoint():
    """每小时创建检查点"""
    global start, state, task_id_buffer
    
    while not _GUARD_EXIT.wait(3600):
        try:
            if start and state is not None:
                now = datetime.datetime.now()
                # 正常结束当前段并开始新段
                _close_previous_segment(now)
                _start_new_segment(state, now, task_id_buffer)
        except Exception as e:
            log(f'[ERR] 小时检查点失败: {e}')

def _get_default_values_from_task_key():
    """从当前任务标识解析出默认值"""
    global _current_task_key
    
    defaults = {
        'project_name': '',
        'work_type': '', 
        'task_id': '',
        'workload2': ''  # ★★★ 新增：工作量2
    }
    
    if not _current_task_key:
        return defaults
    
    try:
        # 解析任务标识字符串，如 "ID:123|类型:数据更新|项目:测试项目|框量:100"
        parts = _current_task_key.split('|')
        for part in parts:
            if part.startswith('项目:'):
                defaults['project_name'] = part[3:]  # 去掉"项目:"前缀
            elif part.startswith('类型:'):
                defaults['work_type'] = part[3:]     # 去掉"类型:"前缀
            elif part.startswith('ID:'):
                defaults['task_id'] = part[3:]       # 去掉"ID:"前缀
            elif part.startswith('框量:'):  # ★★★ 新增：解析框量
                defaults['workload2'] = part[3:]     # 去掉"框量:"前缀
        
    except Exception as e:
        log(f'[ERR] 解析任务标识失败: {e}')
    
    return defaults

def set_state_direct(st, project_name, work_type, task_id, workload1='', workload2='', remark='', manual=True):
    """直接设置状态和任务信息，不弹窗询问"""
    global state, start, task_id_buffer, _current_task_key, switch_type_buffer, _state_task_id
    
    try:
        print(f'[DEBUG-set_state_direct] 开始: st={st}, project={project_name}, task_id={task_id}')
        
        now = datetime.datetime.now()
        
        # 1. 结束当前时间段
        if start:
            print('[DEBUG-set_state_direct] 结束当前时间段')
            _close_previous_segment(now)
        
        # 2. 生成任务标识
        new_task_key = get_task_key(project_name, work_type, task_id, workload2)
        if new_task_key is None:
            print('[DEBUG-set_state_direct] 任务标识生成失败：必填字段不足')
            return False
        
        # 3. 更新任务标识和全局变量
        _current_task_key = new_task_key
        task_id_buffer = task_id
        switch_type_buffer = '手动' if manual else '自动'
        
        if st in WORK_STATES:
            _state_task_id[st] = task_id
        
        # 4. 保存任务标识
        print('[DEBUG-set_state_direct] 保存任务标识')
        _save_task_identifier()
        
        # 5. 开始新的时间段
        print('[DEBUG-set_state_direct] 开始新的时间段')
        
        # 调用 flush_one_record 时传入 workload2 参数
        date = now.strftime('%Y-%m-%d')
        fname = os.path.join(_log_dir, f'{date}_{_USER_ID}.csv')
        
        from services.file_operations import flush_one_record
        print(f'[DEBUG-set_state_direct] 调用 flush_one_record: {project_name}, {work_type}, {task_id}, {workload1}, {workload2}')
        
        flush_one_record(
            fname, _USER_ID, st, now, None, 0, date,
            task_id, remark, switch_type_buffer,
            project_name, work_type, workload1, workload2
        )
        
        state, start = st, now
        
        # 6. 刷新显示
        print('[DEBUG-set_state_direct] 准备刷新显示')
        try:
            from core.signal_hub import fire_state_changed
            fire_state_changed(state)
            print('[DEBUG-set_state_direct] 显示刷新完成')
        except Exception as e:
            print(f'[ERR] 刷新显示失败: {e}')
        
        print(f'[DEBUG-set_state_direct] 直接设置状态完成: {st}, 任务标识: {_current_task_key}')
        return True
        
    except Exception as e:
        print(f'[ERR-set_state_direct] 失败: {e}')
        import traceback
        traceback.print_exc()
        return False